﻿using Common.Type;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Configuration;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Common.Data.Extensions
{
    public class DBConfig<T> : EntityTypeConfiguration<T> where T : class
    {
        public DBConfig(string schema = "dbo", ConvertType namingType = ConvertType.CAPITALIZED, string tablePrefix = null, bool isMappingDefault = true, bool isSplitTable = true, bool isSplitColumn = true)
        {
            if (isMappingDefault)
            {
                var vtype = typeof(T);
                var tableName = vtype.Name;
                var configName = ConfigurationManager.AppSettings[vtype.Name];
                if (!string.IsNullOrWhiteSpace(configName))//Check Mapping
                {
                    tableName = configName.Trim();
                    var indexPads = tableName.IndexOf('.');
                    if (indexPads > 0 && indexPads < tableName.Length - 1)
                    {
                        schema = tableName.Substring(0, indexPads);
                        tableName = tableName.Substring(indexPads + 1);
                    }
                }
                else if (isSplitTable)
                {
                    tableName = AddCharsCapitalized(tableName, '_');
                }

                if (!string.IsNullOrWhiteSpace(tablePrefix))
                {
                    tableName = tablePrefix + tableName;
                }

                switch (namingType)
                {
                    case ConvertType.UPPER:
                        ToTable(tableName.ToUpper(), schema);
                        var propertiesUp = vtype.GetProperties();
                        foreach (var prop in propertiesUp)
                        {
                            object[] attrs = prop.GetCustomAttributes(false);
                            if (attrs.Any(x => x.ToString() == "System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute")) continue;
                            var proName = prop.Name;
                            var configProName = ConfigurationManager.AppSettings[string.Format("{0}.{1}", vtype.Name, proName)];
                            if (!string.IsNullOrWhiteSpace(configProName))
                            {
                                proName = configProName.Trim();
                            }
                            else if (isSplitColumn)
                            {
                                proName = AddCharsCapitalized(proName, '_');
                            }
                            proName = proName.ToUpper();
                            var param = Expression.Parameter(typeof(T));
                            var propertyExpression = Expression.Lambda(Expression.Property(param, prop.Name), param);

                            if (prop.PropertyType == typeof(string))
                            {
                                Property((Expression<Func<T, string>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(Guid))
                            {
                                Property((Expression<Func<T, Guid>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(Guid?))
                            {
                                Property((Expression<Func<T, Guid?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(DateTime))
                            {
                                Property((Expression<Func<T, DateTime>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(DateTime?))
                            {
                                Property((Expression<Func<T, DateTime?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(decimal))
                            {
                                Property((Expression<Func<T, decimal>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(decimal?))
                            {
                                Property((Expression<Func<T, decimal?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(double))
                            {
                                Property((Expression<Func<T, double>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(double?))
                            {
                                Property((Expression<Func<T, double?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(float))
                            {
                                Property((Expression<Func<T, float>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(float?))
                            {
                                Property((Expression<Func<T, float?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(long))
                            {
                                Property((Expression<Func<T, long>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(long?))
                            {
                                Property((Expression<Func<T, long?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(int))
                            {
                                Property((Expression<Func<T, int>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(int?))
                            {
                                Property((Expression<Func<T, int?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(short))
                            {
                                Property((Expression<Func<T, short>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(short?))
                            {
                                Property((Expression<Func<T, short?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(byte))
                            {
                                Property((Expression<Func<T, byte>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(byte?))
                            {
                                Property((Expression<Func<T, byte?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(bool))
                            {
                                Property((Expression<Func<T, bool>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(bool?))
                            {
                                Property((Expression<Func<T, bool?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                        }
                        break;

                    case ConvertType.LOWER:
                        ToTable(tableName.ToLower(), schema);
                        var propertiesLow = vtype.GetProperties();
                        foreach (var prop in propertiesLow)
                        {
                            object[] attrs = prop.GetCustomAttributes(false);
                            if (attrs.Any(x => x.ToString() == "System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute")) continue;
                            var proName = prop.Name.ToLower();
                            var configProName = ConfigurationManager.AppSettings[string.Format("{0}.{1}", vtype.Name, proName)];
                            if (!string.IsNullOrWhiteSpace(configProName))
                            {
                                proName = configProName.Trim();
                            }
                            else if (isSplitColumn)
                            {
                                proName = AddCharsCapitalized(proName, '_');
                            }

                            var param = Expression.Parameter(typeof(T));
                            var propertyExpression = Expression.Lambda(Expression.Property(param, prop.Name), param);

                            if (prop.PropertyType == typeof(string))
                            {
                                Property((Expression<Func<T, string>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(Guid))
                            {
                                Property((Expression<Func<T, Guid>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(Guid?))
                            {
                                Property((Expression<Func<T, Guid?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(DateTime))
                            {
                                Property((Expression<Func<T, DateTime>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(DateTime?))
                            {
                                Property((Expression<Func<T, DateTime?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(decimal))
                            {
                                Property((Expression<Func<T, decimal>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(decimal?))
                            {
                                Property((Expression<Func<T, decimal?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(double))
                            {
                                Property((Expression<Func<T, double>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(double?))
                            {
                                Property((Expression<Func<T, double?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(float))
                            {
                                Property((Expression<Func<T, float>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(float?))
                            {
                                Property((Expression<Func<T, float?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(long))
                            {
                                Property((Expression<Func<T, long>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(long?))
                            {
                                Property((Expression<Func<T, long?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(int))
                            {
                                Property((Expression<Func<T, int>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(int?))
                            {
                                Property((Expression<Func<T, int?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(short))
                            {
                                Property((Expression<Func<T, short>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(short?))
                            {
                                Property((Expression<Func<T, short?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(byte))
                            {
                                Property((Expression<Func<T, byte>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(byte?))
                            {
                                Property((Expression<Func<T, byte?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(bool))
                            {
                                Property((Expression<Func<T, bool>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                            else if (prop.PropertyType == typeof(bool?))
                            {
                                Property((Expression<Func<T, bool?>>)propertyExpression).HasColumnName(proName).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
                            }
                        }
                        break;
                }
            }
        }

        private string AddCharsCapitalized(string text, char splitChar)
        {
            if (string.IsNullOrWhiteSpace(text))
                return text;
            StringBuilder newText = new StringBuilder();
            newText.Append(text[0]);
            for (int i = 1; i < text.Length; i++)
            {
                if (char.IsUpper(text[i]) && text[i - 1] != splitChar)
                    newText.Append(splitChar);
                newText.Append(text[i]);
            }
            return newText.ToString();
        }
    }
}
